#!/usr/bin/python
import xmpp
import traceback
import os
import time
#import gui

from ini import *

# This file deals with all the XMPP side of things
# It creates a "Bot" object which deals with taking commands and turning
# them into xmpp stanzas and sending them, as well as providing functions
# to override when events occur.

# You are expected to override functions beginning with "ev_"

def _promote_jid(jid):
	"""takes a string/unicode string and returns a JID object, returns 
the argument if it is already a jid"""
	if isinstance(jid,xmpp.protocol.JID):
		return jid
	return xmpp.protocol.JID(jid=jid)
	
class Bot:
	def __init__(self,username,password,domain="gmail.com"):
		self.jid=xmpp.protocol.JID("%s@%s" % (username,domain))
		self.password=password
		self.timerevents = {}
		
# Things to do
	def connect(self, server = None, use_srv = None, resource = ''):
		"""Connect to the server"""
		self.client=xmpp.Client(
				self.jid.getDomain(),
				debug=[])

		if server:
			self.client.connect(server, use_srv = use_srv)
		else:
			self.client.connect()
		del self.client.disconnect_handlers[0]
		self.client.RegisterHandler('message',self._msgcb)
		self.client.RegisterHandler('iq',self._iqcb)
		self.client.RegisterHandler('presence',self._presencecb)
		self.client.auth(self.jid.getNode(),self.password, resource)
		self.client.sendInitPresence()

	def run(self):
		"""Run the bot, accepting commands until stop() called"""
		self.running = True
		last_ping = last_activity = last_break = time.time()
		
		if core.optional.gui == "Bacon":
			app = gui.MyApp(0)
		
		while self.running:
			try:
				#Montior day changes and changes in log location.
				if not os.path.isdir(core.optional.logpath):
					os.mkdir(core.optional.logpath)
					print "ALERT: Log directory doesn't exist, making folder \""+ core.optional.logpath +"\""
				global logf
				logf = file(os.path.join(core.optional.logpath, time.strftime(core.optional.logformat) + '.log'), "a+")
			
				# Send some kind of dummy message every few minutes to 
				# tell Google we're still here.
				if time.time() - last_ping > 120: # every 2 minutes
					# Say we're online.
					self.setOnline(module.system.status)
					last_ping = time.time()
			
				#Run through the timer list and run any events
				#that haven't been run in their defined interval.
				for event in self.timerevents:
					interval = self.timerevents[event][0]
					last_run = self.timerevents[event][1]
					repeat = self.timerevents[event][2] 
					if time.time() - last_run > interval and repeat != 0:
						self.timerevents[event][1] = time.time()
						event(self)
						if repeat > 0:
							self.timerevents[event][2] -= 1
			
				#print self.client.Process(1)
				self.client.Process(1)
			except KeyboardInterrupt, e:
				break
			except AttributeError:
				traceback.print_exc()
				self.stop()
				if self.jid.getDomain() == "gmail.com":
					self.connect(("talk.google.com",5223), use_srv = False, resource = 'conference')
				else:
					self.connect()
				self.run()
			except:
				traceback.print_exc()
				continue

	def disconnect(self):
		"Disconnect and stop the bot"
		self.client.disconnect()
		self.stop()

	def stop(self):
		"Stop the bot"
		self.running=False
		
	def addTimer(self, time, event, repeat = -1, type = "minutes"):
		"""Add an event to run at a set number of minutes."""
		if type.lower() == "hours":
			time = time * 60 * 60
		elif type.lower() == "minutes":
			time = time * 60
		self.timerevents[event] = [time, 0, repeat]
		
	def delTimer(self, event):
		"""Delete an event's timer instance."""
		try:
			del self.timerevents[event]
		except:
			pass

# Messages to send
	def msg(self,jid,message):
		"Send a message to the specified jid"
		last_activity = time.time()
		self.client.send(xmpp.protocol.Message(jid,message))

# Roster management commands
	def addUser(self,jid):
		"Asks a user to join your roster"
		self.client.send(xmpp.protocol.Presence(
					to=jid, typ='subscribe'))

	def removeUser(self,jid):
		"Removes a user from your roster"
		self.client.send(xmpp.protocol.Presence(
					to=jid, typ='unsubscribe'))

	def acceptUser(self,jid):
		"Allow a user to add you to their roster"
		self.client.send(xmpp.protocol.Presence(
					to=jid, typ='subscribed'))

	def rejectUser(self,jid):
		"Remove yourself from a remote users roster/disallow adding"
		self.client.send(xmpp.protocol.Presence(
					to=jid, typ='unsubscribed'))

	def DisconnectHandler():
		pass
					
	def _getSingleJidStatus(self,roster,jid):
		"Internal: Figures out the status for a fully qualifed jid"
		# Subscribing
		jid=unicode(jid)
		if roster.getAsk(jid)=="subscribe":
			return u"subscribe",u""
		show={
			"away" : u"away",
			None : u"online",
			"xa" : u"xa",
			"dnd" : u"dnd",
			"chat" : u"chat",
		}[roster.getShow(jid)]
		status=roster.getStatus(jid)
		if status is None:
			status=u""
		return show,status

	def getJidStatus(self,jid):
		"Returns a dict of all this users resources to a tuple of their status (subscribe/away/online/xa/dnd/chat) and message"
		roster=self.client.getRoster()
		jid=_promote_jid(jid)
		if jid.getResource()=='':
			resources=roster.getResources(unicode(jid))
		else:
			resources=[jid.getResource()]
		ret={}
		for resource in resources:
			fjid=xmpp.protocol.JID(
				node=jid.getNode(),
				domain=jid.getDomain(),
				resource=resource)
			ret[fjid]=self._getSingleJidStatus(roster,fjid)
		return ret

	def getRoster(self):
		"Return all the users in the roster"
		roster=self.client.getRoster()
		return roster.getItems()

# Change bots status
	def setOnline(self,msg=None,to=None):
		"Set the bot 'Online'"
		self.client.send(xmpp.protocol.Presence(to=to,status=msg))

	def setUnavailable(self,msg=None,to=None):
		"Set the bot 'Offline'"
		self.client.send(xmpp.protocol.Presence(
					to=to,status=msg,show='unavailable'))

	def setAway(self,msg=None,to=None):
		"Set the bot 'Away'"
		self.client.send(xmpp.protocol.Presence(
					to=to,status=msg,show='away'))

	def setDND(self,msg=None,to=None):
		"Set the bot 'Do Not Disturb'"
		self.client.send(xmpp.protocol.Presence(
					to=to,status=msg,show='dnd'))

	def setXA(self,msg=None,to=None):
		"Set the bot 'eXtended Away'"
		self.client.send(xmpp.protocol.Presence(
					to=to,status=msg,show='xa'))

# Message related events
	def ev_msg(self,user,text):
		"Override me: Called with new messages"
		pass

# Presence related events
	def ev_subscribe(self,user,msg):
		"User requests to add you to their roster"
		pass

	def ev_subscribed(self,user,msg):
		"User has been added to the bots roster"
		pass

	def ev_unavailable(self,user,msg):
		"User went offline"
		pass

	def ev_unsubscribe(self,user,msg):
		"User requestes removal from their roster"
		pass

	def ev_unsubscribed(self,user,msg):
		"User has been removed from the bots roster"
		pass

	def ev_online(self,user,msg):
		"User is now online"
		pass

	def ev_away(self,user,msg):
		"User is away"
		pass

	def ev_chat(self,user,msg):
		"User is interested in chatting"
		pass
	
	def ev_dnd(self,user,msg):
		"User is Do Not Disturb"
		pass

	def ev_xa(self,user,msg):
		"User is eXtended Away"
		pass

# Queries
	def ev_iq(self,user,iq):
		"Information Query"
		pass

# Internal XMPP callbacks
	def _msgcb(self,conn,mess):
		"Internal: Recieve a message from the server"
		if mess.getError() != None:
			if mess.getError() != "recipient-unavailable":
				print "Message Error:", mess.getError()
			return
			
		text = mess.getBody()
		user = mess.getFrom()
		
		#Prevents "NoneTypes" from causing errors.
		if text != None:
			self.ev_msg(user,text)

	def _presencecb(self,conn,pres):
		presTypes1={
			"subscribe"	: self.ev_subscribe,
			"subscribed"	: self.ev_subscribed,
		}
		presTypes2={
			"unsubscribe"	: self.ev_unsubscribe,
			"unsubscribed"	: self.ev_unsubscribed,
			"unavailable" 	: self.ev_unavailable,
		}
		presShows={ 
			"away" 	: self.ev_away,
			"chat"	: self.ev_chat,
			"dnd"	: self.ev_dnd,
			"xa"	: self.ev_xa,
		}

		user=pres.getFrom()

		msg=pres.getStatus()
		if msg is None:
			msg = ""

		flag = 0

		# Deal with subscription etc
		if presTypes1.has_key(pres.getType()):
			presTypes1[pres.getType()](user,msg)
			flag = 1

		# Deal with away/chat/dnd/xa
		if presShows.has_key(pres.getShow()):
			presShows[pres.getShow()](user,msg)
			flag = 1

		# they're "just" online
		if pres.getType() is None and pres.getShow() is None:
			self.ev_online(user,msg)
			flag = 1

		# Deal with unsubscription etc
		if presTypes2.has_key(pres.getType()):
			presTypes2[pres.getType()](user,msg)
			flag = 1

		if flag == 0:
			print "unknown:",user,"/",`pres.getType()`,"/",`pres.getShow()`,"/",`pres.getStatus()`,"/",msg

	def _iqcb(self,conn,iq):
		self.ev_iq(iq.getFrom(),iq)
